/*
 * Copyright (C) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import LightWeightSet from '@ohos.util.LightWeightSet';
import LightWeightMap from '@ohos.util.LightWeightMap';
import { Subscription } from './Subscription';
import { References } from '../listener/References';
import HashMap from '@ohos.util.HashMap';
import { Utils } from '../utils/Utils';
import { SubscriptionFactory } from './SubscriptionFactory';
import { BusRuntime } from '../bus/BusRuntime';
import { MetadataReader } from '../listener/MetadataReader';

/**
 * 管理订阅
 */

export class SubscriptionManager {
    private static sInstance: SubscriptionManager;
    private referenceMap: HashMap<String, References>;
    private nonListeners: LightWeightSet<Object>;
    private subscriptionPerListener: LightWeightMap<String, Array<Subscription>>;
    private runtime: BusRuntime;
    private subscriptionFactory: SubscriptionFactory;
    private metadataReader: MetadataReader;
    private mClassName: String;

    constructor(metadataReader: MetadataReader, factory: SubscriptionFactory, runtime: BusRuntime) {
        this.referenceMap = new HashMap<String, References>();
        this.nonListeners = new LightWeightSet<Object>();
        this.subscriptionPerListener = new LightWeightMap<String, Array<Subscription>>();
        this.runtime = runtime;
        this.subscriptionFactory = factory;
        this.metadataReader = metadataReader;
    }

    public subscribe(listener: Object): void {
        //添加listener
        if (this.nonListeners.has(listener)) {
            return;
        }
        let className = Utils.getClassName(listener);
        this.mClassName = className;
        //根据listener获取sub对象
        let isStrong: boolean = this.metadataReader.useStrongReferences(className);
        let subscriptionArray = this.getSubscriptionsByListener(listener);
        if (!subscriptionArray) {
            let handleMethods = this.metadataReader.getMethodHandle(className);
            if (!handleMethods || handleMethods.length == 0) {
                this.nonListeners.add(listener);
                return;
            }
            subscriptionArray = new Array<Subscription>();
            handleMethods.forEach((parameter, index) => {
                subscriptionArray.push(this.subscriptionFactory.createSubscription(this.runtime, parameter, isStrong));
            })
            subscriptionArray.forEach((value, index) => {
                value.subscribe(listener);
            })
            this.subscriptionPerListener.set(className, subscriptionArray);
        } else {
            subscriptionArray.forEach((value, index) => {
                value.subscribe(listener);
            })
        }
    }

    public unsubscribe(listener: Object):boolean {
        if (!listener) {
            return false;
        }
        let subscriptions = this.getSubscriptionsByListener(listener);
        if (!subscriptions || subscriptions.length == 0) {
            return false;
        }
        let isRemoved: boolean = true;
        subscriptions.forEach((v, i) => {
            isRemoved = isRemoved && v.unsubscribe(listener);
        })
        return isRemoved;
    }

    public getSubscriptionsByListener(listener: Object): Array<Subscription> | undefined {
        if (!listener) {
            return undefined;
        }
        var className = Utils.getClassName(listener);
        if (this.subscriptionPerListener.hasKey(className)) {
            return this.subscriptionPerListener.get(className);
        }
        return undefined;
    }

    public getSubscriptionsByClassName(): Array<Subscription> | undefined {
        if (!this.mClassName) {
            return undefined;
        }
        if (this.subscriptionPerListener.hasKey(this.mClassName)) {
            return this.subscriptionPerListener.get(this.mClassName);
        }
        return undefined;
    }

    public setReferences(className: String, rType: References) {
        if (this.referenceMap) {
            this.referenceMap.set(className, rType);
        }
    }
}